/*
 * Importer.java
 *
 * Created on September 11, 2007, 9:36 AM
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */

package org.atomojo.tools;

import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.atomojo.app.client.Entry;
import org.atomojo.app.client.EntryClient;
import org.atomojo.app.client.FeedClient;
import org.atomojo.app.client.FeedDestination;
import org.atomojo.app.client.StatusException;
import org.infoset.xml.Document;
import org.infoset.xml.Element;
import org.infoset.xml.InfosetFactory;
import org.infoset.xml.XMLException;
import org.restlet.data.MediaType;
import org.restlet.data.Metadata;
import org.restlet.data.Status;
import org.restlet.representation.InputRepresentation;
import org.restlet.service.MetadataService;

/**
 *
 * @author alex
 */
public class DirectorySynchronizer
{
   
   static class Target {
      File dir;
      URI feed;
      Target(File dir,URI feed)
      {
         this.dir = dir;
         this.feed = feed;
      }
   }
   
   URI baseFeed;
   File directory;
   boolean exact;
   boolean excludeDot;
   Logger log;
   
   String username;
   String password;
   
   /** Creates a new instance of Importer */
   public DirectorySynchronizer(File directory,URI baseFeed)
   {
      this.baseFeed = baseFeed;
      this.directory = directory;
      this.exact = false;
      this.log = Logger.getLogger(DirectorySynchronizer.class.getName());
      this.excludeDot = false;
      this.username = null;
      this.password = null;
   }
   
   public void setIdentity(String username,String password) {
      this.username = username;
      this.password = password;
   }
   
   public Logger getLogger() {
      return log;
   }
   
   public void setLogger(Logger log)
   {
      this.log = log;
   }
   
   public void setExact(boolean flag)
   {
      this.exact = flag;
   }
   
   public boolean isExact() {
      return this.exact;
   }
   
   public void setExcludingDotFiles(boolean flag)
   {
      this.excludeDot = flag;
   }
   
   public boolean isExcludingDotFiles() {
      return this.excludeDot;
   }
   
   public void setDirectory(File dir)
   {
      this.directory = dir;
   }
   
   public File getDirectory() {
      return this.directory;
   }
   
   public void sync() 
   {
      final List<Target> targets = new ArrayList<Target>();
      targets.add(new Target(directory,baseFeed));
      while (targets.size()>0) {
         final Target target = targets.remove(0);
         log.info("Syncing directory "+target.dir+" with feed "+target.feed);
         
         final FeedClient feedClient = new FeedClient(target.feed);
         feedClient.setIdentity(username,password);
         if (!feedClient.exists()) {
            log.info("Creating feed "+target.feed);
            try {
               Document doc = InfosetFactory.getDefaultInfoset().createItemConstructor().createDocument();
               Element feed = doc.createDocumentElement(FeedClient.FEED_NAME);
               Element title = feed.addElement(FeedClient.TITLE_NAME);
               title.addCharacters(target.dir.getName());
               Status status = feedClient.create(doc);
               if (!status.isSuccess()) {
                  log.log(Level.SEVERE,"Cannot create feed "+target.feed+" due to error "+status.getCode());
                  continue;
               }
            } catch (XMLException ex) {
               log.log(Level.SEVERE,"Cannot create feed "+target.feed+" due to exception.",ex);
               continue;
            }
         }
         
         final Map<String,Entry> entries = new HashMap<String,Entry>();
         try {
            feedClient.get(new FeedDestination() {
               public void onFeed(Document feedDoc) {}
               public void onEntry(Document entryDoc) {
                  Entry entry = new Entry(entryDoc);
                  Entry.Media media = entry.getMediaContent();
                  if (media!=null) {
                     //log.info("Caching entry for "+media.getName());
                     entries.put(media.getName(),entry);
                  }
               }
            });
         } catch (XMLException ex) {
            log.log(Level.SEVERE,"Cannot get feed "+target.feed+" due to XML exception.",ex);
            continue;
         } catch (IOException ex) {
            log.log(Level.SEVERE,"Cannot get feed "+target.feed+" due to I/O exception.",ex);
            continue;
         }
         
         String u = target.feed.toString();
         if (u.charAt(u.length()-1)!='/') {
            u += "/";
         }
         final String feedBase = u;
         final MetadataService metadataService = new MetadataService();
         target.dir.listFiles(new FileFilter() {
            public boolean accept(File file) {
               if (file.isDirectory()) {
                  URI subFeed = URI.create(feedBase+file.getName()+"/");
                  targets.add(new Target(file,subFeed));
               } else {
                  if (!excludeDot || file.getName().charAt(0)!='.') {
                     // Sync content
                     Entry entry = entries.get(file.getName());
                     Status status = null;
                     if (entry!=null) {
                        log.info("Updating "+file);
                        // update existing media
                        try {
                           FileInputStream is = new FileInputStream(file);
                           EntryClient client = feedClient.getEntryClient(entry);
                           status = client.updateMedia(new InputRepresentation(is,entry.getMediaContent().getMediaType()));
                           is.close();
                        } catch (IOException ex) {
                           log.log(Level.SEVERE,"Cannot update "+file+" due to I/O exception.",ex);
                        }
                        entries.remove(file.getName());
                     } else {
                        log.info("Creating "+file);
                        // create new media
                        try {
                           FileInputStream is = new FileInputStream(file);
                           int extPos = file.getName().lastIndexOf('.');
                           String ext = extPos<0 ? null : file.getName().substring(extPos+1);
                           Metadata metadata = ext==null ? null : metadataService.getMetadata(ext);
                           MediaType type = metadata==null ? MediaType.APPLICATION_OCTET_STREAM : MediaType.valueOf(metadata.getName());
                           Entry mediaEntry = feedClient.createMedia(file.getName(),new InputRepresentation(is,type));
                           is.close();
                        } catch (StatusException ex) {
                           log.log(Level.SEVERE,"Cannot create media entry from "+file+" due to status "+ex.getStatus().getCode(),ex);
                        } catch (Exception ex) {
                           log.log(Level.SEVERE,"Cannot create media entry from "+file+" due to exception.",ex);
                        }
                     }
                     if (status!=null && !status.isSuccess()) {
                        log.severe("Cannot update/create media entry for "+file+" due to status "+status.getCode());
                     }
                  }
               }
               return false;
            }
         });
         if (exact) {
            for (Entry entry : entries.values()) {
               EntryClient client = feedClient.getEntryClient(entry);
               Status status = client.delete();
               if (!status.isSuccess()) {
                  log.severe("Cannot delete extra entry "+entry.getId()+" due to status "+status.getCode());
               }
            }
         }
      }
   }
   
   
}
